#!/usr/bin/env python

import sys, os, shutil, re

def define(config, what):
  opp_config = open(config, "r+")
  lines = opp_config.readlines()
  opp_config.close()
  i = 0
  for line in lines:
    if re.search(what, line):
      lines[i] = "#define " + what + "\n"
      break
    i = i + 1
  opp_config = open(config, "w")
  opp_config.writelines(lines)
  opp_config.close()

def undefine(config, what):
  opp_config = open(config, "r+")
  lines = opp_config.readlines()
  opp_config.close()
  i = 0
  for line in lines:
    if re.search(what, line):
      lines[i] = "#undef " + what + "\n"
      break
    i = i + 1
  opp_config = open(config, "w")
  opp_config.writelines(lines)
  opp_config.close()

def modify_config(config, regexp, val):
  opp_config = open(config, "r+")
  lines = opp_config.readlines()
  for line in lines:
    if re.match(regexp, line):
      opp_config.seek(len(line) - 2, 1)
      opp_config.write(val)
      break
    opp_config.seek(len(line), 1)
  opp_config.close()

def get_result_time(tmp, regexp, i):
  tmp.seek(0)
  lines = tmp.readlines()
  for line in lines:
    if re.search(regexp, line):
      return re.split("\W+", line)[i]

# Configuration section
string_base = os.path.realpath(__file__)
string_base = string_base[:-12]
string_base += "bin/"
string_list = ["G30000", "G80000", "G150000", "G1600000", "G10000000"]
step_list = ["one", "log"]
hoard_list = ["nohoard"]
hoard_path = ""
bison_vs_opp_list = [1, 2, 4, 8]
MAX_CORES = 2 

# Get input parameters
try:
  samples = int(sys.argv[1])
  out_path = sys.argv[2]
except:
  print "tester> Insufficient arguments provided."
  sys.exit(-1)

print "tester> samples %d, out_path %s" % (samples, out_path)

# Reset out_path
if os.path.exists(out_path):
  shutil.rmtree(out_path)
os.makedirs(out_path)

config_file_name = string_base[:-9] + "include/config.h"
undefine(config_file_name, "DEBUG")
undefine(config_file_name, "TEST")
undefine(config_file_name, "CHRONO")

# Launch Bison
undefine(config_file_name, "DEBUG")
os.chdir("../tools/bison")
if os.system("make clean > /dev/null && make > /dev/null") != 0:
  print "tester> could not compile bison\n"
  sys.exit(-1)

bison = dict()
for string in string_list:
  print "tester> launching Bison on string %s" % string
  os.system("./parser %s 2> tmp" % (string_base + string))
  tmp = open("tmp", "r")
  bison[string] = long(get_result_time(tmp, "time", 2))
  tmp.close()
os.unlink("tmp")

# Create directories
os.chdir("../../test")
for hoard in hoard_list:
  os.mkdir(out_path + "/" + hoard)
  os.mkdir(out_path + "/" + hoard + "/flex")
  os.mkdir(out_path + "/" + hoard + "/noflex")

# Launch parser on strings
average = dict()
for string in string_list:
  print "\ntester> analyzing string %s\n" % string
  average[string] = dict()
  for hoard in hoard_list:
    average[string][hoard] = dict()
    if hoard == "hoard":
      print "tester> using Hoard allocator"
      os.environ["LD_PRELOAD"] = hoard_path
    else:
      print "tester> not using Hoard allocator"
      os.environ["LD_PRELOAD"] = ""
    print "tester> LD_PRELOAD: %s" % os.environ["LD_PRELOAD"]

    for step in step_list:
      average[string][hoard][step] = dict()
      print "tester> recombination step %s" % step
      # Set STEP in config.h
      if step == "log":
        modify_config(config_file_name, "#define STEP", "0")
      else:
        modify_config(config_file_name, "#define STEP", "1")

      os.chdir("..")
      if os.system("make clean > /dev/null && make > /dev/null") != 0:
        print "tester> could not compile libopp"
        sys.exit(-1)
      print "tester> compiled libopp"
      os.chdir("test")
      if os.system("make clean > /dev/null && make > /dev/null && strip bin/opp") != 0:
        print "tester> could not compile opp"
        sys.exit(-1)
      print "tester> compiled opp"

      for i in range(1, MAX_CORES + 1):
        file_time = open(out_path + "/" + hoard + "/noflex/CORE" + str(i) + "_" + string + "_" + str(samples) + "_" + step.upper() + ".dat", "a")
        file_flex = open(out_path + "/" + hoard + "/flex/CORE" + str(i) + "_" + string + "_" + str(samples) + "_" + step.upper() + ".dat", "a")
        aggr_time = open(out_path + "/" + hoard + "/noflex/CORE" + str(i) + "_" + str(samples) + "_" + step.upper() + "_AGGR.dat", "a")
        aggr_flex = open(out_path + "/" + hoard + "/flex/CORE" + str(i) + "_" + str(samples) + "_" + step.upper() + "_AGGR.dat", "a")

        average[string][hoard][step][i] = dict()

        sum_times = 0
        sum_flex_times = 0
        sys.stdout.write("tester> launching opp -j%d\t%s %s %s sample " % (i, string_base + string, hoard, step))
        sys.stdout.flush()

        for j in range(1, samples + 1):
          sys.stdout.write("%03d/%03d\b\b\b\b\b\b\b" % (j, samples))
          sys.stdout.flush()
          os.system("bin/opp -j%d %s > tmp" % (i, string_base + string))
          tmp = open("tmp", "r")
          flex_timer = long(get_result_time(tmp, "lex", 1))
          timer = long(get_result_time(tmp, "time", 1))
          tmp.close()
          sum_times += timer
          sum_flex_times += flex_timer
          file_time.write("%d\t%f\n" % (j, timer))
          file_flex.write("%d\t%f\n" % (j, flex_timer))

        sys.stdout.write("\n")
        file_time.close()
        file_flex.close()
        # Compute averages
        average[string][hoard][step][i]["noflex"] = sum_times/float(samples)
        average[string][hoard][step][i]["flex"] = sum_flex_times/float(samples)
        aggr_time.write("%s\t%f\n" % (string, sum_times/float(samples)))
        aggr_flex.write("%s\t%f\n" % (string, sum_flex_times/float(samples)))
        aggr_time.close()
        aggr_flex.close()
  # Compute speedups
  print "tester> computing speedups on string %s" % string
  speedup_file = open(out_path + "/" + string + "_speedup.dat", "w")
  speedup_file.write("%s" % "threads".ljust(7))
  for step in step_list:
    for hoard in hoard_list:
      speedup_file.write("\t%s" % (step + "_" + hoard).rjust(12))
  speedup_file.write("\n")
  for i in range(1, MAX_CORES + 1):
    speedup_file.write("%s" % str(i).ljust(7))
    for step in step_list:
      for hoard in hoard_list:
        speedup_file.write("\t%12f" % (average[string][hoard][step][1]["noflex"]/average[string][hoard][step][i]["noflex"]))
    speedup_file.write("\n")
  speedup_file.close()

  # Compute bison_vs_opp
  print "tester> computing bison vs opp comparison on string %s" % string
  bison_vs_opp_file = open(out_path + "/" + string + "_bison_vs_opp.dat", "w")
  normalized_file = open(out_path + "/" + string + "_bison_vs_opp_normalized.dat", "w")
  bison_vs_opp_file.write("%s" % "threads".ljust(7))
  normalized_file.write("%s" % "threads".ljust(7))
  for step in step_list:
    for hoard in hoard_list:
      bison_vs_opp_file.write("\t%s" % (step + "_" + hoard).rjust(12))
      normalized_file.write("\t%s" % (step + "_" + hoard).rjust(12))
  bison_vs_opp_file.write("\n")
  normalized_file.write("\n")
  bison_vs_opp_file.write("%s" % "bison".ljust(7))
  normalized_file.write("%s" % "bison".ljust(7))
  for step in step_list:
    for hoard in hoard_list:
      bison_vs_opp_file.write("\t%12d" % bison[string])
      normalized_file.write("\t%12f" % 1)
  bison_vs_opp_file.write("\n")
  normalized_file.write("\n")
  for i in bison_vs_opp_list:
    bison_vs_opp_file.write("%s" % str(i).ljust(7))
    normalized_file.write("%s" % str(i).ljust(7))
    for step in step_list:
      for hoard in hoard_list:
        bison_vs_opp_file.write("\t%12d" % average[string][hoard][step][i]["flex"])
        normalized_file.write("\t%12f" % (float(average[string][hoard][step][i]["flex"])/float(bison[string])))
    bison_vs_opp_file.write("\n")
    normalized_file.write("\n")
  bison_vs_opp_file.close()
  normalized_file.close()

os.unlink("tmp")

